#pragma once
// OpenGL and GLEW Header Files and Libraries
#include <GL/glew.h>
#include <GL/wglew.h>
#include <GL/glfw.h>


#include "Utility.h"

#include "OBJ.h"
#include "Camera.h"

#include <conio.h>

#pragma comment(lib, "KinectWinLib.lib")

float fps =0;
float currentTime, pastTime; 

struct Debug {
	int selectedEntity;
};
/*
Rendering Elements
*/
std::vector<glm::vec3> aLightsPos;
std::vector<glm::vec3> aLightsCol;
std::vector<float> aLightsIntensivity;

std::map<std::string, std::vector<glm::vec3>> allTangents, allBitangents, allVerticesPerMaterial, allNormalsPerMaterial;
std::map<std::string, std::vector<glm::vec2>> allUVsPerMaterial;

std::vector<glm::vec3> ves; // used for boxing

/* 
Rendering Elements
*/

GLuint VertexArrayID;
GLuint BoundinGBoxesID;

std::map<std::string, Material> Materials = std::map<std::string, Material>(); 

std::map<std::string, Mesh> Model;

ProgramHandles Programs;
ShaderUniforms Uniforms;
BoundingUniform BoundingUniforms;
Camera* cam;
Scene scene;

Debug debug;

//Kinect Controller configs
//hand need to be in small rectangle
struct KConf {
	float minX;
	float maxX;
	float minY;
	float maxY;
	float timeToWait;
	float timeLeftToWait;
	bool active;
	float timeLeftToInactive;
	float timeToInactive;
	float prevPosX;
	float prevPosY;
	float orginX;
	float orginY;
} kConf;




int trianglesDisplayed;

//GLuint texture;
GLuint vertexbuffer[255], uvbuffer[255], normalbuffer[255], tangentbuffer[255], bitangentbuffer[255];

//one buffer for all boxes!!
GLuint boxesBuffer;

/*
Ray Caster
*/
struct Ray {
	glm::vec3 Orgin;
	glm::vec3 Dir;
};


bool IntersectBoxTest(Ray r, BoundingBox aabb, float t0, float t1)
{
	float tmin, tmax, tymin, tymax, tzmin, tzmax;

	if(r.Dir.x >= 0 ) {
		tmin = (aabb.min.x - r.Orgin.x ) / r.Dir.x;
		tmax = (aabb.max.x - r.Orgin.x ) / r.Dir.x;
	}else {
		tmin = (aabb.max.x - r.Orgin.x ) / r.Dir.x;
		tmax = (aabb.min.x - r.Orgin.x ) / r.Dir.x;
	}
	if(r.Dir.y >= 0 ) {
		tymin = (aabb.min.y - r.Orgin.y ) / r.Dir.y;
		tymax = (aabb.max.y - r.Orgin.y ) / r.Dir.y;
	}else {
		tymin = (aabb.max.y - r.Orgin.y ) / r.Dir.y;
		tymax = (aabb.min.y - r.Orgin.y ) / r.Dir.y;
	}
	if((tmin > tymax) || (tymin > tmax))
		return false;

	if(tymin > tmin)
		tmin = tymin;
	if (tymax < tmax)
		tmax = tymax;

	if(r.Dir.z >= 0) {
		tzmin = (aabb.min.z - r.Orgin.z) / r.Dir.z;
		tzmax = (aabb.max.z - r.Orgin.z) / r.Dir.z;
	}else {
		tzmin = (aabb.max.z - r.Orgin.z) / r.Dir.z;
		tzmax = (aabb.min.z - r.Orgin.z) / r.Dir.z;
	
	}

	if((tmin > tzmax) || (tzmin > tmax))
		return false;

	if(tzmin > tmin)
		tmin = tzmin;
	if(tzmax < tmax)
		tmax = tzmax;

	return ((tmin < t1) && (tmax > t0));
}

void computeTangentBasis(
	std::vector<glm::vec3> & vertices,
	std::vector<glm::vec2> & uvs,
	std::vector<glm::vec3> & normals,
	//output
	std::vector<glm::vec3> & tangents,
	std::vector<glm::vec3> & bitangents)
{
	for(int i = 0; i<vertices.size(); i+=3)
	{

		// Shortcuts for vertices
		glm::vec3 & v0 = vertices[i+0];
		glm::vec3 & v1 = vertices[i+1];
		glm::vec3 & v2 = vertices[i+2];
 
		// Shortcuts for UVs
		glm::vec2 & uv0 = uvs[i+0];
		glm::vec2 & uv1 = uvs[i+1];
		glm::vec2 & uv2 = uvs[i+2];
 
		// Edges of the triangle : postion delta
		glm::vec3 deltaPos1 = v1-v0;
		glm::vec3 deltaPos2 = v2-v0;
 
		// UV delta
		glm::vec2 deltaUV1 = uv1-uv0;
		glm::vec2 deltaUV2 = uv2-uv0;

		float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
		glm::vec3 tangent = (deltaPos1 * deltaUV2.y   - deltaPos2 * deltaUV1.y)*r;
		glm::vec3 bitangent = (deltaPos2 * deltaUV1.x   - deltaPos1 * deltaUV2.x)*r;

		tangents.push_back(tangent);
		tangents.push_back(tangent);
		tangents.push_back(tangent);
		
		bitangents.push_back(bitangent);
		bitangents.push_back(bitangent);
		bitangents.push_back(bitangent);

	}

}

void resize(int w, int h)
{
	
	uScrWidth = w;
	uScrHeight = h;
    glViewport(0,0,(GLsizei)w, (GLsizei)h);
    gluPerspective(fov, (GLfloat)w/ (GLfloat)h , nearPlane, farPlane);
 	
}

void initShaders()
{
	Programs.Main = LoadProgram("data/shaders/lukes.vert", "data/shaders/lukes.frag");

	

	Uniforms.MVP = glGetUniformLocation(Programs.Main, "MVP");
	Uniforms.V = glGetUniformLocation(Programs.Main, "V");
	Uniforms.M = glGetUniformLocation(Programs.Main, "M");
	Uniforms.NoLights = glGetUniformLocation(Programs.Main, "NoLights");
	Uniforms.MV3x3 = glGetUniformLocation(Programs.Main, "MV3x3");

	Uniforms.AmbientColor = glGetUniformLocation(Programs.Main, "AmbientColor");
	Uniforms.DifuseColor = glGetUniformLocation(Programs.Main, "DifuseColor");
	Uniforms.SpecularColor = glGetUniformLocation(Programs.Main, "SpecularColor");
	//Uniforms.Shines = glGetUniformLocation(Programs.Main, "Shines");
	
	Uniforms.DiffuseTextureSampler = glGetUniformLocation(Programs.Main, "DiffuseTextureSampler");
	Uniforms.AmbientTextureSampler = glGetUniformLocation(Programs.Main, "AmbientTextureSampler");
	Uniforms.NormalTextureSampler = glGetUniformLocation(Programs.Main, "NormalTextureSampler");
	//Uniforms.SpecularTextureSampler = glGetUniformLocation(Programs.Main, "SpecularTextureSampler");

	Uniforms.LightPosition = glGetUniformLocation(Programs.Main, "LightPosition");
	Uniforms.LightColor = glGetUniformLocation(Programs.Main, "LightColor");
	Uniforms.LightPower = glGetUniformLocation(Programs.Main, "LightPower");
	
	Programs.BoundingBoxes =  LoadProgram("data/shaders/bbox.vert", "data/shaders/bbox.frag"); // LoadProgram("data/shaders/bounding.vert", "data/shaders/bounding.frag");
	BoundingUniforms.MVP =  glGetUniformLocation(Programs.BoundingBoxes, "MVP");
	BoundingUniforms.MVP =  glGetUniformLocation(Programs.BoundingBoxes, "M");
	

}

static int addLight(lua_State *lua)
{

	glm::vec3 pos;
	pos.x = lua_tonumber(lua,1); 
	pos.y = lua_tonumber(lua,2); 
	pos.z = lua_tonumber(lua,3);

	glm::vec3 color;
	color.x = lua_tonumber(lua,4); 
	color.y = lua_tonumber(lua,5); 
	color.z = lua_tonumber(lua,6);

	float intensivity = lua_tonumber(lua,7);
	int id = lua_tonumber(lua, 8);
	id--;

	scene.lights[id].lightsPos = pos;
	scene.lights[id].lightsCol = color;
	scene.lights[id].lightsIntensivity = intensivity;
	scene.lights[id].alive = true;
	
	std::cout << "light added! \n";

	return 0;
}

static int addMaterial(lua_State *lua)
{
	Material temp;

	size_t x = 128;
	temp.name =  lua_tolstring(lua,1, &x);

	std::string t = "data/textures/";
	std::string tex = t;
	tex.append(lua_tostring(lua, 2, &x));

	std::string nTex = t;
	nTex.append(lua_tostring(lua, 3, &x));

	std::string sTex = t;
	sTex.append(lua_tostring(lua, 4, &x));

	float vx = lua_tonumber(lua,5);
	float vy = lua_tonumber(lua,6);
	float vz = lua_tonumber(lua,7);

	temp.AmbiantColor = glm::vec3(vx,vy,vz);
	temp.Shinies = lua_tonumber(lua,8);

	temp.AmbientTexture =  loadTGA_glfw(tex.c_str());
	temp.NormalTexture = loadTGA_glfw(nTex.c_str());
	temp.SpecularTexture = loadTGA_glfw(sTex.c_str());

	Materials[temp.name] = temp;

	std::cout << "material added! \n";

	return 0;
}

Material getMaterialByName(std::string name)
{
	std::map<std::string, Material>::iterator iter = Materials.find(name);
    if(iter != Materials.end())
    {
            return iter->second;
    }
	Material temp;
	return temp;
}

static int addObject(lua_State *lua)
{
	Mesh temp;
	std::map<std::string, subMesh> tempSubMeshs;
	//temp.subMeshes; // = std::map<std::string, Material>();

	size_t x = 128;
	std::string name = lua_tostring(lua, 1, &x);


	//check if model is in our list already
	std::map<std::string, Mesh>::iterator iter = Model.find(name);
    if(iter != Model.end()) {
		return 0;
	}else {
		std::cout << "adding " << name.c_str() << " to resources \n"; 
	}

	std::string path = "data/models/";
	path.append(lua_tostring(lua, 2, &x));

	if(loadOBJ(path.c_str(), tempSubMeshs))
	{
		std::cout << path.c_str() << " Loaded! \n";
//		temp.material = getMaterialByName(lua_tostring(lua,3,&x));
	}

	std::map<std::string, subMesh>::iterator ip = tempSubMeshs.begin();
	for(; ip != tempSubMeshs.end(); ++ip)
	{
		Materials[ip->second.material.name] = ip->second.material; 
	}

	temp.subMeshes = tempSubMeshs;

	Model[name] = temp;

	return 0;
	
}

static int addEntity(lua_State *lua)
{
	Entity tmp; 
	int id = lua_tonumber(lua, 1);
	id--;
	//check if model is in our list already
	std::map<int, Entity>::iterator iter = scene.entities.find(id);
    if(iter != scene.entities.end()) {
		return 0;
	}else {
		std::cout << "adding entity id: " << id << " to scene \n"; 
	}

	size_t x = 128;

	tmp.modelUsed= lua_tostring(lua, 2, &x);

	tmp.position = glm::vec3(lua_tonumber(lua, 3), lua_tonumber(lua, 4), lua_tonumber(lua, 5));
	tmp.rotation = glm::vec3(lua_tonumber(lua, 6), lua_tonumber(lua, 7), lua_tonumber(lua, 8));
	tmp.scale = glm::vec3(lua_tonumber(lua, 9), lua_tonumber(lua, 10), lua_tonumber(lua, 11));
	tmp.selected = false;

	scene.entities[id] = tmp;

	std::cout << "done! \n";
	return 0;
}

bool initLUA(const char* name)
{
	int iErr = false;
	lua_State *lua = lua_open();
	luaL_openlibs(lua);

	if((iErr = luaL_loadfile (lua, name)) == 0)
	{
		
		lua_pushcclosure(lua, addLight, 0);
		lua_setglobal (lua, "addLight");

		lua_pushcclosure(lua, addMaterial, 0);
		lua_setglobal (lua, "addMaterial");

		lua_pushcclosure(lua, addObject, 0);
		lua_setglobal (lua, "addObject");

		lua_pushcclosure(lua, addEntity, 0);
		lua_setglobal (lua, "addEntity");

		//Call main
		if((iErr = lua_pcall(lua, 0, LUA_MULTRET, 0)) == 0)
		{
			//Push the function name onto the stack
			lua_pushstring(lua, "initWorld");

			lua_gettable (lua, LUA_GLOBALSINDEX);
			lua_pcall(lua, 0,0,0);

		}else{
			return false;
		}
	}else {
		return false;
	}
	lua_close(lua);
	return true;
}
void prepareBoundingLinesFor(int i)
{
	//clear prev
	ves.clear();
	
	glGenBuffers(1, &boxesBuffer);
	glm::vec3 t;
		
	//- - -
	ves.push_back(scene.entities[i].box.min);
		
	//+ - -
	t.x = scene.entities[i].box.max.x;
	t.y = scene.entities[i].box.min.y;
	t.z = scene.entities[i].box.min.z;
	ves.push_back(t);
	ves.push_back(t);

		//+ - +
	t.x = scene.entities[i].box.max.x;
	t.y = scene.entities[i].box.min.y;
	t.z = scene.entities[i].box.max.z;
	ves.push_back(t);
	ves.push_back(t);

	//- - +
	t.x = scene.entities[i].box.min.x;
	t.y = scene.entities[i].box.min.y;
	t.z = scene.entities[i].box.max.z;
	ves.push_back(t);
	ves.push_back(t);

	// - - -
	ves.push_back(scene.entities[i].box.min);
	ves.push_back(scene.entities[i].box.min);

	//- + -
	t.x = scene.entities[i].box.min.x;
	t.y = scene.entities[i].box.max.y;
	t.z = scene.entities[i].box.min.z;
	ves.push_back(t);
	ves.push_back(t);

	//+ + -
	t.x = scene.entities[i].box.max.x;
	t.y = scene.entities[i].box.max.y;
	t.z = scene.entities[i].box.min.z;
	ves.push_back(t);
	ves.push_back(t);

	//+ + +
	ves.push_back(scene.entities[i].box.max);
	ves.push_back(scene.entities[i].box.max);
		
	//- + +
	t.x = scene.entities[i].box.min.x;
	t.y = scene.entities[i].box.max.y;
	t.z = scene.entities[i].box.max.z;
	ves.push_back(t);
	ves.push_back(t);

	//- + -
	t.x = scene.entities[i].box.min.x;
	t.y = scene.entities[i].box.max.y;
	t.z = scene.entities[i].box.min.z;
	ves.push_back(t);


	// line between -++ and --+
	t.x = scene.entities[i].box.min.x;
	t.y = scene.entities[i].box.max.y;
	t.z = scene.entities[i].box.max.z;
	ves.push_back(t);		

	t.x = scene.entities[i].box.min.x;
	t.y = scene.entities[i].box.min.y;
	t.z = scene.entities[i].box.max.z;
	ves.push_back(t);		

	// line between +++ and +-+
	t.x = scene.entities[i].box.max.x;
	t.y = scene.entities[i].box.max.y;
	t.z = scene.entities[i].box.max.z;
	ves.push_back(t);		

	t.x = scene.entities[i].box.max.x;
	t.y = scene.entities[i].box.min.y;
	t.z = scene.entities[i].box.max.z;
	ves.push_back(t);	

	// line between ++- and +--
	t.x = scene.entities[i].box.max.x;
	t.y = scene.entities[i].box.max.y;
	t.z = scene.entities[i].box.min.z;
	ves.push_back(t);		

	t.x = scene.entities[i].box.max.x;
	t.y = scene.entities[i].box.min.y;
	t.z = scene.entities[i].box.min.z;
	ves.push_back(t);		
	

	for(int i = 0; i < ves.size(); i ++)
	{

		std::cout << "Box [" << i << "/" << ves.size() << ": " << ves[i].x << " " << ves[i].y << " " << ves[i].z << "\n";
	}
		
	glBindBuffer(GL_ARRAY_BUFFER, boxesBuffer);
	int st =  ves.size() * sizeof(glm::vec3);
	glBufferData(GL_ARRAY_BUFFER, st, &ves[0], GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	std::cout << "Bounding Boxes finished! \n";

}


glm::vec3 scale(1);

void update(float dt)
{
//	kConf.timeLeftToWait = kConf.timeToWait;
	
//	kConf.minX = uScrWidth/2 - uScrWidth/12;
//	kConf.maxX = uScrWidth/2 + uScrWidth/12;
//	kConf.minY = uScrHeight/2 - uScrHeight/12;
//	kConf.maxY = uScrHeight/2 + uScrHeight/12;
	int xpos, ypos;
	glfwGetMousePos(&xpos, &ypos);

	if(kConf.active == false)
	{
		if(kConf.minX < (float)xpos && (float)xpos < kConf.maxX && kConf.minY < (float)ypos && (float)ypos < kConf.maxY)
		{
			kConf.timeLeftToWait -= dt;
			if(kConf.timeLeftToWait <= 0)
			{
				kConf.active = true;
				kConf.timeLeftToWait = kConf.timeToWait;
				kConf.orginX = xpos;
				kConf.orginY = ypos;
			}
		}else {
			kConf.timeLeftToWait = kConf.timeToWait;
			kConf.timeLeftToInactive = kConf.timeToInactive; 
		}
	}

	if(kConf.active == true)
	{
		if(xpos == kConf.prevPosX && ypos == kConf.prevPosY)
		{
			kConf.timeLeftToInactive -= dt;
		}else 
		{
			kConf.timeLeftToInactive = kConf.timeToInactive;
		}

		if(kConf.timeLeftToInactive <= 0)
		{
			kConf.active = false;
			//need to be made only once
			cam->mTransform.Model = glm::mat4(1);

			// Reset mouse position so it will not be in activation box
			glfwSetMousePos(0, 0);
		}
	}

	
	
	if(kConf.active == true)
	{
		int e = -(kConf.prevPosX - xpos) / 2;
		scene.entities[0].rotation.z = e;
		cam->mTransform.Model = glm::rotate(cam->mTransform.Model, (float)e, glm::vec3(0,0,1));
		cam->mTransform.Model = glm::scale(cam->mTransform.Model, glm::vec3(1));
		if( ypos != kConf.prevPosY)
		{
			float s = (kConf.prevPosY - ypos);
		
			

			if(s < 0)
			{
				s = 0;
			}

			if(s >= 1.00)
			{
				s = 0.980;

			}


			//make it smaller

			if(kConf.prevPosY < ypos)
			{
				s = 1.028;
			}

			scale = glm::vec3(s);
			cam->mTransform.Model = glm::scale(cam->mTransform.Model, scale);
		}

	}else {
		cam->mTransform.Model = glm::rotate(cam->mTransform.Model, (float)0.1, glm::vec3(0,0,1));
	}
	cam->update();

	//cam->mTransform.Model;
	
	if (glfwGetKey( GLFW_KEY_SPACE ) == GLFW_PRESS ){
		std::cout << "Intersection test\n";

		Ray ray;
		ray.Dir = cam->castRay();
		ray.Orgin = cam->mPosition;

		for(int i = 0; i < scene.entities.size(); i++)
		{
			scene.entities[i].selected = false;
			if(IntersectBoxTest(ray, scene.entities[i].box, 0, 100))
			{
				debug.selectedEntity = i;
				scene.entities[i].selected = true;
				prepareBoundingLinesFor(i);
				std::cout << "Colided with entity #"<< i << "\n";
			}
		}
	}

	kConf.prevPosX = xpos;
	kConf.prevPosY = ypos;
	
}

void displayInfoAboutSelectedEntity()
{
	
}


void displayFrustrumCoordinates()
{
	

}

void render2DText()
{
	
	//displayInfoAboutSelectedEntity();
	//displayFrustrumCoordinates();
}




void createBuffers()
{
	//for each light

	std::map<int, Light>::iterator iterL = scene.lights.begin();
	for(; iterL != scene.lights.end(); ++iterL)
	{
		if(iterL->second.alive == true)
		{
			aLightsPos.push_back(iterL->second.lightsPos);
			aLightsCol.push_back(iterL->second.lightsCol);
			aLightsIntensivity.push_back(iterL->second.lightsIntensivity);
		}
	}

	for(int i= 0; i < scene.entities.size(); i++)
	{
		glm::vec4 tm(1);
		//for each mesh
		std::map<std::string, Mesh>::iterator iter = Model.find(scene.entities[i].modelUsed);
		if(iter != Model.end())
		{
			scene.entities[i].box.min.x = 9999999;
			scene.entities[i].box.max.x = -9999999;
			scene.entities[i].box.min.y = 9999999;
			scene.entities[i].box.max.y = -9999999;
			scene.entities[i].box.min.z = 9999999;
			scene.entities[i].box.max.z = -9999999;

			//for each sub mesh
			std::map<std::string, subMesh>::iterator iterSub = iter->second.subMeshes.begin();
			for(; iterSub != iter->second.subMeshes.end(); ++iterSub)
			{
				std::vector<glm::vec3>::iterator iit = iterSub->second.vertices.begin();
				for(; iit != iterSub->second.vertices.end(); ++iit)
				{
					tm.x = iit->x;
					tm.y = iit->y;
					tm.z = iit->z;
					tm.w = 1;

					glm::mat4 T = glm::mat4(1);
					
				

					glm::mat4 Rot = glm::rotate(T, scene.entities[i].rotation.x, glm::vec3(1.0,0,0));  
					tm =  Rot  * tm;
					Rot = glm::rotate(T, scene.entities[i].rotation.y, glm::vec3(0,1.0,0));  
					tm =  Rot  * tm;
					Rot = glm::rotate(T, scene.entities[i].rotation.z, glm::vec3(0,0,1.0));  
					tm =  Rot  * tm;

					glm::mat4 Sc = glm::mat4();
					
					Sc = glm::scale(Sc, scene.entities[i].scale);

					tm = Sc * tm;

					glm::mat4 Tr = glm::translate(T, glm::vec3(0, 0, i*100));

					tm = Tr * tm;

					

					allVerticesPerMaterial[iterSub->second.material.name].push_back(glm::vec3(tm.x, tm.y, tm.z));


					if(tm.x < scene.entities[i].box.min.x )
					{
						scene.entities[i].box.min.x = tm.x;
					}else if(tm.x >= scene.entities[i].box.max.x )
					{
						scene.entities[i].box.max.x = tm.x;
					}

					if(tm.y < scene.entities[i].box.min.y )
					{
						scene.entities[i].box.min.y = tm.y;
					}else if(tm.y >= scene.entities[i].box.max.y )
					{
						scene.entities[i].box.max.y = tm.y;
					}
					
					if(tm.z < scene.entities[i].box.min.z )
					{
						scene.entities[i].box.min.z = tm.z;
					}else if(tm.z >= scene.entities[i].box.max.z )
					{
						scene.entities[i].box.max.z = tm.z;
					}

				}

				iit = iterSub->second.normals.begin();
				for(; iit != iterSub->second.normals.end(); ++iit)
				{
					glm::vec3 tm(iit->x, iit->y, iit->z);

					allNormalsPerMaterial[iterSub->second.material.name].push_back(tm); 

				}

				std::vector<glm::vec2>::iterator iitt = iterSub->second.uvs.begin();
				for(; iitt != iterSub->second.uvs.end(); ++iitt)
				{
					glm::vec2 tm(iitt->x, iitt->y);

					allUVsPerMaterial[iterSub->second.material.name].push_back(tm);
				}

				computeTangentBasis(allVerticesPerMaterial[iterSub->second.material.name], allUVsPerMaterial[iterSub->second.material.name], allNormalsPerMaterial[iterSub->second.material.name], allTangents[iterSub->second.material.name], allBitangents[iterSub->second.material.name]);
			}
		}
	
	}
	std::cout << "createBuffers finished! \n";
}



void initVAO() {

	glGenVertexArrays(1, &VertexArrayID);
	glBindVertexArray(VertexArrayID);

	createBuffers();
	
	glGenBuffers(Materials.size(), &normalbuffer[0]);
	
	glGenBuffers(Materials.size(), &vertexbuffer[0]);
	
	glGenBuffers(Materials.size(), &uvbuffer[0]);

	glGenBuffers(Materials.size(), &tangentbuffer[0]);

	glGenBuffers(Materials.size(), &bitangentbuffer[0]);
	
			
	std::map<std::string, Material>::iterator iter = Materials.begin();
	int i =0;
	for(; iter != Materials.end(); ++iter)
	{
		//need to change that to one vbo per each material
		glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer[i]);
		int t =  allVerticesPerMaterial[iter->first].size() *sizeof(glm::vec3);
		std::string st = iter->first;
		glBufferData(GL_ARRAY_BUFFER, allVerticesPerMaterial[iter->first].size() *sizeof(glm::vec3), &allVerticesPerMaterial[iter->first][0], GL_STATIC_DRAW);
		
		glBindBuffer(GL_ARRAY_BUFFER, uvbuffer[i]);
		glBufferData(GL_ARRAY_BUFFER, allUVsPerMaterial[iter->first].size() * sizeof(glm::vec2), &allUVsPerMaterial[iter->first][0], GL_STATIC_DRAW);
		
		glBindBuffer(GL_ARRAY_BUFFER, normalbuffer[i]);
		glBufferData(GL_ARRAY_BUFFER, allNormalsPerMaterial[iter->first].size() * sizeof(glm::vec3), &allNormalsPerMaterial[iter->first][0], GL_STATIC_DRAW);
		
		glBindBuffer(GL_ARRAY_BUFFER, tangentbuffer[i]);
		glBufferData(GL_ARRAY_BUFFER, allTangents[iter->first].size() * sizeof(glm::vec3), &allTangents[iter->first][0], GL_STATIC_DRAW);

		glBindBuffer(GL_ARRAY_BUFFER, bitangentbuffer[i]);
		glBufferData(GL_ARRAY_BUFFER, allBitangents[iter->first].size() * sizeof(glm::vec3), &allBitangents[iter->first][0], GL_STATIC_DRAW);

		i++;
	}
	std::cout << "initVAO finished! \n";

	

	
}

void init()
{
	initDebuger();

	debug.selectedEntity = -999;

	glm::vec3 temp(0);
	cam = new Camera(temp);


	kConf.timeToWait = 45;
	kConf.timeLeftToWait = kConf.timeToWait;
	
	kConf.minX = uScrWidth/2 - uScrWidth/12;
	kConf.maxX = uScrWidth/2 + uScrWidth/12;
	kConf.minY = uScrHeight/2 - uScrHeight/12;
	kConf.maxY = uScrHeight/2 + uScrHeight/12;
	kConf.active = false;

	kConf.timeToInactive = 100;
	kConf.timeLeftToInactive = kConf.timeToInactive;

	initLUA("data/scripts/general.lua");

	initShaders();
	initVAO();

	glEnable(GL_DEPTH_TEST); // We enable the depth test (also called z buffer)
    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); // Polygon rasterization mode (polygon filled)	
	
	// Initialize our little text library with the Holstein font
	initText2D( "data/fonts/standard.tga" );
	std::cout << "Init finished! \n";
}

void setUniforms()
{
	glUniformMatrix4fv(Uniforms.MVP, 1, GL_FALSE, &cam->mTransform.MVP[0][0]);
	glUniformMatrix4fv(Uniforms.M, 1, GL_FALSE, &cam->mTransform.Model[0][0]);
	glUniformMatrix4fv(Uniforms.V, 1, GL_FALSE, &cam->mTransform.View[0][0]);
	glUniformMatrix3fv(Uniforms.MV3x3, 1, GL_FALSE, &cam->mTransform.ModelView3x3Matrix[0][0]);

	int nol = scene.lights.size();
	
	glUniform1i(Uniforms.NoLights, nol);


	glUniform3fv(Uniforms.LightPosition,sizeof(glm::vec3),&aLightsPos[0][0]);
	glUniform3fv(Uniforms.LightColor,sizeof(glm::vec3), &aLightsCol[0][0]);
	glUniform1fv(Uniforms.LightPower,nol, &aLightsIntensivity[0]);

	
}

void setUniformsAABB()
{
	//for bounding boxes
	glUniformMatrix4fv(BoundingUniforms.MVP, 1, GL_FALSE, &cam->mTransform.MVP[0][0]);
	glUniformMatrix4fv(BoundingUniforms.M, 1, GL_FALSE, &cam->mTransform.MVP[0][0]);

}

void render()
{
	trianglesDisplayed = 0;
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 
	// Use our shader
	glUseProgram(Programs.Main);

	//GLuint MaterialSpecularColorInID = glGetUniformLocation(Programs.Main, "MaterialSpecularColorIn");

	setUniforms();
	

	//drawing Model
	std::map<std::string, Material>::iterator iter = Materials.begin();
	int i =0;
	for(; iter != Materials.end(); ++iter)
	{
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, Materials[iter->first].DifuseTexture);
		glUniform1i(Uniforms.DiffuseTextureSampler, 0);


		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, Materials[iter->first].AmbientTexture);
		glUniform1i(Uniforms.AmbientTextureSampler, 1);


		
		glActiveTexture(GL_TEXTURE2);
		glBindTexture(GL_TEXTURE_2D, Materials[iter->first].NormalTexture);
		glUniform1i(Uniforms.NormalTextureSampler, 2);

		glActiveTexture(GL_TEXTURE3);
		glBindTexture(GL_TEXTURE_2D, Materials[iter->first].SpecularTexture);
		glUniform1i(Uniforms.SpecularTextureSampler, 3);

		glActiveTexture(0);

		glUniform3fv(Uniforms.AmbientColor, sizeof(glm::vec3), &Materials[iter->first].AmbiantColor[0]);
		glUniform3fv(Uniforms.DifuseColor, sizeof(glm::vec3), &Materials[iter->first].DifuseColor[0]);
		glUniform3fv(Uniforms.SpecularColor, sizeof(glm::vec3), &Materials[iter->first].SpecularColor[0]);
		glUniform1f(Uniforms.Shines, Materials[iter->first].Shinies);
	

		// 1rst attribute buffer : vertices
		glEnableVertexAttribArray(0);
		glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer[i]);
		glVertexAttribPointer(
			0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
			3,                  // size
			GL_FLOAT,           // type
			GL_FALSE,           // normalized?
			0,                  // stride
			(void*)0            // array buffer offset
		);
		
		// 2nd attribute buffer : normals
		glEnableVertexAttribArray(1);
		glBindBuffer(GL_ARRAY_BUFFER, uvbuffer[i]);
		glVertexAttribPointer(
		1,                                // attribute. No particular reason for 1, but must match the layout in the shader.
		2,                                // size
		GL_FLOAT,                         // type
		GL_FALSE,                         // normalized?
		0,                                // stride
		(void*)0                          // array buffer offset
		);

		// 2nd attribute buffer : textcord
		glEnableVertexAttribArray(2);
		glBindBuffer(GL_ARRAY_BUFFER, normalbuffer[i]);
		glVertexAttribPointer(
			2,                                // attribute. No particular reason for 1, but must match the layout in the shader.
			3,                                // size
			GL_FLOAT,                         // type
			GL_FALSE,                         // normalized?
			0,                                // stride
			(void*)0                          // array buffer offset
		);

		glEnableVertexAttribArray(3);
		glBindBuffer(GL_ARRAY_BUFFER, tangentbuffer[i]);
		glVertexAttribPointer(
			3,                                // attribute
			3,                                // size
			GL_FLOAT,                         // type
			GL_FALSE,                         // normalized?
			0,                                // stride
			(void*)0                          // array buffer offset
		);

		glEnableVertexAttribArray(4);
		glBindBuffer(GL_ARRAY_BUFFER, bitangentbuffer[i]);
		glVertexAttribPointer(
			4,                                // attribute
			3,                                // size
			GL_FLOAT,                         // type
			GL_FALSE,                         // normalized?
			0,                                // stride
			(void*)0                          // array buffer offset
		);




		int s =  allVerticesPerMaterial[iter->first].size();
		glDrawArrays(GL_POINTS, 0, s);

		trianglesDisplayed = trianglesDisplayed+s;
		
		glDisableVertexAttribArray(0);
		glDisableVertexAttribArray(1);
		glDisableVertexAttribArray(2);
		glDisableVertexAttribArray(3);
		glDisableVertexAttribArray(4);

		i++;
	}



	if(ves.size() > 0)
	{
		// 1rst attribute buffer : vertices
		glUseProgram(Programs.BoundingBoxes);

		setUniformsAABB();
	
		glBindBuffer(GL_ARRAY_BUFFER, boxesBuffer);
		glEnableVertexAttribArray(0);
		glVertexAttribPointer(
			0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
			3,                  // size
			GL_FLOAT,           // type
			GL_FALSE,           // normalized?
			0,                  // stride
			(void*)0            // array buffer offset
		);

		glDrawArrays(GL_POINTS, 0, ves.size());
		glDisableVertexAttribArray(0);
	}

	glm::mat4 iden(1);
	renderLightsDebug(scene.lights, cam->mTransform.Projection * cam->mTransform.View * iden);

	render2DText();
//	glBindVertexArray(0);

}

int main()
{
	
	KinectWinMgr kinectMgr;
    if( kinectMgr.NuiInit() )
    {
		std::cout << "Kinect Ready baby!!\n";
	
	}

	

   int frames = 0;
    currentTime = pastTime = 0;
        
    char   title[150];


    int running = GL_TRUE;

		//init glfw
    if(!glfwInit())
    {
            exit(EXIT_FAILURE);
    }

	glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 2); 
	glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3); // We want OpenGL 3.1
	glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR,3);
	glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //We don't want the old OpenGL


    if(!glfwOpenWindow(uScrWidth, uScrHeight, 0,0,0,0,0,0, GLFW_FULLSCREEN ))
    {
            glfwTerminate();
            exit(EXIT_FAILURE);
    }

    glfwSetWindowTitle("nGine by Lukasz Iwanski");
    glfwSetWindowSizeCallback(resize);
		
    // Check GLEW
    GLenum err = glewInit();
    if(GLEW_OK != err)
    {
            std::cout << "Error: " << glewGetErrorString(err) << std::endl;

    }

    if (!GLEW_VERSION_3_3) { /* for some reason GLEW_VERSION_4_0 is not working */
		std::cout << "Error: OpenGL 3.0 is required" << std::endl;
		exit(1);
	}

    std::cout << "Status: Using GLEW " << glewGetString(GLEW_VERSION) << std::endl;
	if (!GLEW_ARB_vertex_buffer_object)
	{
		std::cout << "VBO not supported\n";
		exit(1);
	}

	glEnable(GL_DEPTH_TEST);
	// Accept fragment if it closer to the camera than the former one
	glDepthFunc(GL_LESS); 

	// Cull triangles which normal is not towards the camera
	glfwEnable( GLFW_MOUSE_CURSOR );
	init();
    while(running)
    {

        currentTime = glfwGetTime();

		update(currentTime-pastTime);
            
			
		render();			
			
        
        glfwSwapBuffers();
        running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam(GLFW_OPENED);
                
        if((currentTime-pastTime) > 1.0 || frames == 0)
		{
			fps = (float)frames / (currentTime-pastTime);
			//sprintf(title, "Voxe 2.0 (%.1f FPS)", fps);
			//glfwSetWindowTitle(title);
			pastTime = currentTime;
			frames = 0;
		}
		frames++;
		//Limit the frameRate to 60fps.
        glfwSwapInterval(1);

    }
		
	// Close OpenGL window and terminate GLFW
	glfwTerminate();

	cleanupText2D();

	kinectMgr.NuiUnInit();
    exit(EXIT_SUCCESS);
}
